#include "Arduino.h"
#include <ESP8266WebServer.h>
#include <FS.h>
#include <ArduinoJson.h>
#include "../stats.h"
#include "html.h"
#include "../info/system.h"

extern char *html;

ESP8266WebServer server(80);
File uploadFile;
static int count = 0;

String uploadedFileName;
String uploadedFilePath;
size_t uploadedFileSize;

const char* headerKeys[] = {"Content-Length", "Content-Type"};
size_t headerKeyCount = sizeof(headerKeys) / sizeof(char*);

class FileRequestHandler : public RequestHandler
{
public:
  bool canHandle(HTTPMethod method, const String &uri) override
  {
    return (method == HTTP_GET) && uri.startsWith("/file/");
  }

  bool handle(ESP8266WebServer &server, HTTPMethod requestMethod, const String &requestUri) override
  {
    String filePath = requestUri.substring(strlen("/file"));

    if (filePath.length() == 0)
    {
      server.send(400, "text/plain", "Bad Request");
      return true;
    }
    if (!filePath.startsWith("/"))
    {
      filePath = "/" + filePath;
    }

    Serial.printf("FileRequestHandler: serving %s\n", filePath.c_str());

    bool gzipped = false;

    if (!SPIFFS.exists(filePath))
    {
      server.send(404, "text/plain", "File not found");
      return true;
    }

    File file = SPIFFS.open(filePath, "r");
    if (!file)
    {
      server.send(500, "text/plain", "Failed to open file");
      return true;
    }

    String contentType = "application/octet-stream";

    if (filePath.endsWith(".htm") || filePath.endsWith(".html"))
      contentType = "text/html;charset=utf-8";
    else if (filePath.endsWith(".css"))
      contentType = "text/css";
    else if (filePath.endsWith(".js"))
      contentType = "application/javascript";
    else if (filePath.endsWith(".json"))
      contentType = "application/json";
    else if (filePath.endsWith(".png"))
      contentType = "image/png";
    else if (filePath.endsWith(".gif"))
      contentType = "image/gif";
    else if (filePath.endsWith(".jpg") || filePath.endsWith(".jpeg"))
      contentType = "image/jpeg";
    else if (filePath.endsWith(".ico"))
      contentType = "image/x-icon";
    else if (filePath.endsWith(".txt"))
      contentType = "text/plain;charset=utf-8";
    else if (filePath.endsWith(".gz")){
      gzipped = true;
      contentType = "text/html;charset=utf-8";
    }

    server.streamFile(file, contentType);
    file.close();
    return true; // 表示请求已处理
  }
};

FileRequestHandler fileHandler;

void handleListFiles()
{
  StaticJsonDocument<1024> doc;

  if (!SPIFFS.begin())
  {
    doc["error"] = "SPIFFS not mounted";
    String json;
    serializeJson(doc, json);
    server.send(500, "application/json", json);
    return;
  }

  FSInfo fs_info;
  if (!SPIFFS.info(fs_info))
  {
    doc["error"] = "SPIFFS info failed";
    String json;
    serializeJson(doc, json);
    server.send(500, "application/json", json);
    return;
  }

  doc["total"] = fs_info.totalBytes;
  doc["used"] = fs_info.usedBytes;
  doc["free"] = fs_info.totalBytes - fs_info.usedBytes;

  JsonArray files = doc.createNestedArray("files");

  Dir dir = SPIFFS.openDir("/");

  while (dir.next())
  {
    JsonObject file = files.createNestedObject();
    file["name"] = dir.fileName().substring(1);
    file["size"] = dir.fileSize();
    file["path"] = "/file" + dir.fileName();
  }

  String json;
  serializeJson(doc, json);
  server.send(200, "application/json", json);
}

void handleRoot()
{
  char msg[64];
  server.send(200, "text/html", html);
  snprintf(msg, sizeof(msg), "HTTP Client visited /index.html(%d), millis=%lu", count++, millis());
  print_stats();
  Serial.println(msg);
}

void handleTask()
{
  String body = server.arg("plain");
  StaticJsonDocument<1000> doc;
  DeserializationError error = deserializeJson(doc, body);
  if (error)
  {
    server.send(400, "application/json", "{\"error\":\"Invalid JSON\"}");
    return;
  }

  String jsonStr;
  serializeJson(doc, jsonStr);
  server.send(200, "application/json", jsonStr);
}

void handleUploadFinished()
{
  String json = "{";
  json += "\"filename\":\"" + uploadedFileName + "\",";
  json += "\"path\":\"" + uploadedFilePath + "\",";
  json += "\"size\":" + String(uploadedFileSize);
  json += "}";
  server.send(200, "application/json; charset=utf-8", json);

  // 清理上传状态
  uploadedFileName = "";
  uploadedFilePath = "";
  uploadedFileSize = 0;
}

void handleUploadData()
{
  HTTPUpload &upload = server.upload();

  if (upload.status == UPLOAD_FILE_START)
  {
    FSInfo fs_info;
    SPIFFS.info(fs_info);
    size_t freeSpace = fs_info.totalBytes - fs_info.usedBytes;

    uploadedFileName = upload.filename.substring(upload.filename.lastIndexOf('/') + 1);
    uploadedFilePath = "/" + uploadedFileName;

    size_t contentLength = server.header("Content-Length").toInt();

    Serial.printf("上传开始: %s, 大小: %u, %d\n", upload.filename.c_str(), upload.totalSize, contentLength);

    if (contentLength > 0 && contentLength > freeSpace)
    {
      StaticJsonDocument<128> doc;
      String body;
      doc["error"] = "Insufficient storage";
      doc["required"] = contentLength;
      doc["available"] = freeSpace;
      serializeJson(doc, body);
      server.send(507, "application/json", body);
      return;
    }

    if (upload.totalSize > freeSpace)
    {
      StaticJsonDocument<128> doc;
      String body;
      doc["error"] = "Insufficient storage";
      doc["required"] = upload.totalSize;
      doc["available"] = freeSpace;
      serializeJson(doc, body);
      server.send(507, "application/json", body);
      return;
    }

    if (SPIFFS.exists("/" + upload.filename))
    {
      SPIFFS.remove("/" + upload.filename);
    }

    uploadFile = SPIFFS.open("/" + upload.filename, "w");
  }
  else if (upload.status == UPLOAD_FILE_WRITE)
  {
    if (uploadFile)
    {
      uploadFile.write(upload.buf, upload.currentSize);
    }
  }
  else if (upload.status == UPLOAD_FILE_END)
  {
    if (uploadFile)
    {
      uploadFile.close();
    }
    uploadedFileSize = upload.totalSize;
    Serial.printf("上传完成: %s, 大小: %u\n", upload.filename.c_str(), upload.totalSize);
  }
  else if (upload.status == UPLOAD_FILE_ABORTED)
  {
    if (uploadFile)
    {
      uploadFile.close();
      SPIFFS.remove("/" + upload.filename);
    }
  }
}

void listenGET()
{
  server.collectHeaders(headerKeys, headerKeyCount);
  server.on("/", HTTP_GET, handleRoot);
  server.on("/index.html", HTTP_GET, handleRoot);
  server.on("/spiffs", HTTP_GET, handleListFiles);
  server.on("/files", HTTP_GET, handleListFiles);
  server.on("/file", HTTP_GET, handleListFiles);

  server.on("/clear", HTTP_GET, []()
            {
    SPIFFS.begin();
    Dir dir = SPIFFS.openDir("/");
    while (dir.next()) {
      String filePath = dir.fileName();
      SPIFFS.remove(filePath);
      Serial.printf("Deleted: %s\n", filePath.c_str());
    }
    SPIFFS.end();
      String json = "{";
  json += "\"size\":" + String("\"ok\"");
  json += "}";
  server.send(200, "application/json; charset=utf-8", json); });

  server.on("/info", []()
            {
    server.sendHeader("Access-Control-Allow-Origin", "*");
    server.sendHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
    server.sendHeader("Access-Control-Allow-Headers", "Content-Type");
    std::string visitedCount = std::to_string(count);
    
    DynamicJsonDocument info = getSysInfo();
    info["visited"] = visitedCount;
    String resp;
    serializeJson(info, resp);
    server.send(200, "application/json", resp); });

  server.on("/hello", []()
            {
    char msg[64];
    server.send(200, "text/html", "<h1>Hello from ESP8266!</h1>");
    snprintf(msg, sizeof(msg), "HTTP Client visited /hello(%d), millis=%lu", count++, millis());
    print_stats();
    Serial.println(msg); });
}

void listenPOST()
{
  server.on("/task", HTTP_POST, handleTask);
  server.on("/upload", HTTP_POST, handleUploadFinished, handleUploadData);
}

void listenHTTP()
{
  listenGET();
  listenPOST();

  server.onNotFound([]()
                    {
  String uri = server.uri();
  server.send(404, "text/plain;charset=utf-8", "404: 来到了没有知识的荒原"); });

  server.addHandler(&fileHandler);

  server.begin();
  Serial.println("HTTP started");
}

void handleHTTP()
{
  server.handleClient();
}
